<!DOCTYPE html>
<html>
  <head>
  <meta http-equiv="content-type" content="text/html; charset=utf-8">
  <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" name="viewport">
  <meta name="description" content="tong.li&#39;s blog">
  <meta name="keyword" content="彤哥哥博客，95后技术爱好者,现就职于同程旅行/同程艺龙上海分公司，专注于互联网技术分享的平台。">
  
    <link rel="shortcut icon" href="/css/images/icon.png">
  
  <title>
    
      Spring Cloud-使用高性能的OkHttp库 | 彤哥哥的博客
    
  </title>
  <link href="https://cdn.staticfile.org/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
  <link href="https://cdn.staticfile.org/nprogress/0.2.0/nprogress.min.css" rel="stylesheet">
  <link href="https://cdn.staticfile.org/highlight.js/9.12.0/styles/tomorrow-night.min.css" rel="stylesheet">
  
<link rel="stylesheet" href="/css/style.css">

  
  <script src="https://cdn.staticfile.org/jquery/3.2.1/jquery.min.js"></script>
  <script src="https://cdn.staticfile.org/geopattern/1.2.3/js/geopattern.min.js"></script>
  <script src="https://cdn.staticfile.org/nprogress/0.2.0/nprogress.min.js"></script>
  
    
<script src="/js/qrious.js"></script>

  
  
  
  
    <!-- MathJax support START -->
    <script type="text/x-mathjax-config">
      MathJax.Hub.Config({
        tex2jax: {
          inlineMath: [ ['$','$'], ["\\(","\\)"]  ],
          processEscapes: true,
          skipTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code']
        }
      });
    </script>

    <script type="text/x-mathjax-config">
      MathJax.Hub.Queue(function() {
        var all = MathJax.Hub.getAllJax(), i;
        for (i=0; i < all.length; i += 1) {
          all[i].SourceElement().parentNode.className += ' has-jax';
        }
      });
    </script>
    <script type="text/javascript" src="https://cdn.staticfile.org/mathjax/2.7.5/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
    <!-- MathJax support END -->
  


  
  
    
<script src="/js/local-search.js"></script>


<meta name="generator" content="Hexo 5.4.2"></head>
<div class="wechat-share">
  <img src="/css/images/logo.png" />
</div>
  <body>
    <header class="header fixed-header">
  <div class="header-container">
    <a class="home-link" href="/">
      <div class="logo"></div>
      <span>彤哥哥的博客</span>
    </a>
    <ul class="right-list">
      
        <li class="list-item">
          
            <a href="/" class="item-link">主页</a>
          
        </li>
      
        <li class="list-item">
          
            <a href="/series/" class="item-link">分类</a>
          
        </li>
      
        <li class="list-item">
          
            <a href="/tags/" class="item-link">标签</a>
          
        </li>
      
        <li class="list-item">
          
            <a href="/archives/" class="item-link">归档</a>
          
        </li>
      
        <li class="list-item">
          
            <a href="/project/" class="item-link">项目</a>
          
        </li>
      
        <li class="list-item">
          
            <a href="/about/" class="item-link">关于</a>
          
        </li>
      
      
        <li class="menu-item menu-item-search right-list">
    <a role="button" class="popup-trigger">
        <i class="fa fa-search fa-fw"></i>
    </a>
</li>
      
    </ul>
    <div class="menu">
      <span class="icon-bar"></span>
      <span class="icon-bar"></span>
      <span class="icon-bar"></span>
    </div>
    <div class="menu-mask">
      <ul class="menu-list">
        
          <li class="menu-item">
            
              <a href="/" class="menu-link">主页</a>
            
          </li>
        
          <li class="menu-item">
            
              <a href="/series/" class="menu-link">分类</a>
            
          </li>
        
          <li class="menu-item">
            
              <a href="/tags/" class="menu-link">标签</a>
            
          </li>
        
          <li class="menu-item">
            
              <a href="/archives/" class="menu-link">归档</a>
            
          </li>
        
          <li class="menu-item">
            
              <a href="/project/" class="menu-link">项目</a>
            
          </li>
        
          <li class="menu-item">
            
              <a href="/about/" class="menu-link">关于</a>
            
          </li>
        
      </ul>
    </div>
    
      <div class="search-pop-overlay">
    <div class="popup search-popup">
        <div class="search-header">
            <span class="search-icon">
                <i class="fa fa-search"></i>
            </span>
            <div class="search-input-container">
                <input autocomplete="off" autocapitalize="off"
                    placeholder="Please enter your keyword(s) to search." spellcheck="false"
                    type="search" class="search-input">
            </div>
            <span class="popup-btn-close">
                <i class="fa fa-times-circle"></i>
            </span>
        </div>
        <div id="search-result">
            <div id="no-result">
                <i class="fa fa-spinner fa-pulse fa-5x fa-fw"></i>
            </div>
        </div>
    </div>
</div>
    
  </div>
</header>

    <div id="article-banner">
  <h2>Spring Cloud-使用高性能的OkHttp库</h2>
  <p class="post-date">2018-01-30</p>
  <div class="arrow-down">
    <a href="javascript:;"></a>
  </div>
</div>
<main class="app-body flex-box">
  <!-- Article START -->
  <article class="post-article">
    <section class="markdown-content"><p>最近在做项目优化,研究了Spring Cloud底层源码,Http请求库默认是Apache HttpClient或者JDK自带的HttpURLConnection库.Java标准库提供了HttpURLConnection类来支持HTTP通讯。不过HttpURLConnection本身的API不够友好，所提供的功能也有限。大部分Java程序都选择使用Apache的开源项目HttpClient作为HTTP客户端。ApacheHttpClient库的功能强大，使用率也很高，基本上是Java平台中事实上的标准HTTP客户端,但是做Android的小伙伴早已经淘汰该库了,就是因为其API数量过多过于繁重,使得我们很难在不破坏兼容性的情况下对它进行升级和扩展,因而团队不愿意去维护该库.本章介绍的是由 Square 公司开发的OkHttp，是一个专注于性能和易用性的 HTTP 客户端。<br>OkHttp 库的设计和实现的首要目标是高效.支持SPDY,,可以合并多个到同一个主机的请求, 使用连接池技术减少请求的延迟(如果SPDY是可用的话),使用GZIP压缩减少传输的数据量,缓存响应避免重复的网络请求,当网络出现问题时，OkHttp 会自动重试一个主机的多个IP地址。</p>
<h2 id="OkHttp在Zuul中的应用"><a href="#OkHttp在Zuul中的应用" class="headerlink" title="OkHttp在Zuul中的应用"></a>OkHttp在Zuul中的应用</h2><p>话说至此,有些人可能又疑问?我怎么知道默认底层用的是的Apache Client?<br>我在公司负责Zuul网关模块,Zuul的动态路由转发用Ribbon调用.查看官方文档,如下:</p>
<blockquote>
<p>19.3 Zuul Http Client<br>The default HTTP client used by zuul is now backed by the Apache HTTP Client instead of the deprecated Ribbon RestClient. To use RestClient or to use the okhttp3.OkHttpClient set ribbon.restclient.enabled=true or ribbon.okhttp.enabled=true respectively. If you would like to customize the Apache HTTP client or the OK HTTP client provide a bean of type ClosableHttpClient or OkHttpClient.</p>
</blockquote>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">参照上述官方文档说明,解决方案已经明了,具体实施如下:</span><br><span class="line">1.确保你的项目有okhttp依赖:</span><br><span class="line">&lt;!-- OkHttp --&gt;</span><br><span class="line">&lt;dependency&gt;</span><br><span class="line">	&lt;groupId&gt;com.squareup.okhttp3&lt;/groupId&gt;</span><br><span class="line">	&lt;artifactId&gt;okhttp&lt;/artifactId&gt;</span><br><span class="line">&lt;/dependency&gt;</span><br></pre></td></tr></table></figure>
<ol start="2">
<li>application.yml添加ribbon配置<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">ribbon:</span><br><span class="line">  httpclient:</span><br><span class="line">     enabled: false # 默认开启需要禁用</span><br><span class="line">  okhttp:</span><br><span class="line">     enabled: true</span><br></pre></td></tr></table></figure></li>
<li>测试替换是否成功<br>a,将GateWay服务日志设置为DEBUG模式进行调用,查看日志会有许多有关OkHttp的东西,但日志打印太多,不易捕获,不易观察<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">RibbonCommandFactoryConfiguration.HttpClientRibbonConfiguration matched:</span><br><span class="line"></span><br><span class="line">- AnyNestedCondition 1 matched 1 did not; NestedCondition on RibbonCommandFactoryConfiguration.OnRibbonHttpClientCondition.RibbonProperty@ConditionalOnProperty</span><br><span class="line"> (ribbon.httpclient.enabled) found different value in property &#x27;ribbon.httpclient.enabled&#x27;; NestedCondition on RibbonCommandFactoryConfiguration.OnRibbonHttpClientCondition.ZuulProperty @ConditionalOnProperty</span><br><span class="line"> (zuul.ribbon.httpclient.enabled) matched (RibbonCommandFactoryConfiguration.OnRibbonHttpClientCondition)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">RibbonCommandFactoryConfiguration.OkHttpRibbonConfigurationmatched:</span><br><span class="line"></span><br><span class="line">-@ConditionalOnClass found required class &#x27;okhttp3.OkHttpClient&#x27;;</span><br><span class="line"> @ConditionalOnMissingClass did not find unwanted class (OnClassCondition)</span><br><span class="line"></span><br><span class="line">- AnyNestedCondition 1 matched 1 did not; NestedCondition on RibbonCommandFactoryConfiguration.OnRibbonOkHttpClientCondition.RibbonProperty@ConditionalOnProperty</span><br><span class="line"> (ribbon.okhttp.enabled) matched; NestedCondition on RibbonCommandFactoryConfiguration.OnRibbonOkHttpClientCondition.ZuulProperty @ConditionalOnProperty (zuul.ribbon.okhttp.enabled) did not find</span><br><span class="line"> property &#x27;zuul.ribbon.okhttp.enabled&#x27; (RibbonCommandFactoryConfiguration.OnRibbonOkHttpClientCondition)</span><br></pre></td></tr></table></figure>
b,Debug启动GateWay服务,通过源码方式查看RibbonCommandFactoryConfiguration实际配置的Http客户端<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br></pre></td><td class="code"><pre><span class="line">package org.springframework.cloud.netflix.zuul;</span><br><span class="line"></span><br><span class="line">import java.lang.annotation.Documented;</span><br><span class="line">import java.lang.annotation.ElementType;</span><br><span class="line">import java.lang.annotation.Retention;</span><br><span class="line">import java.lang.annotation.RetentionPolicy;</span><br><span class="line">import java.lang.annotation.Target;</span><br><span class="line">import java.util.Collections;</span><br><span class="line">import java.util.Set;</span><br><span class="line"></span><br><span class="line">import org.springframework.beans.factory.annotation.Autowired;</span><br><span class="line">import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;</span><br><span class="line">import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;</span><br><span class="line">import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;</span><br><span class="line">import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;</span><br><span class="line">import org.springframework.cloud.netflix.ribbon.SpringClientFactory;</span><br><span class="line">import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;</span><br><span class="line">import org.springframework.cloud.netflix.zuul.filters.route.RestClientRibbonCommandFactory;</span><br><span class="line">import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandFactory;</span><br><span class="line">import org.springframework.cloud.netflix.zuul.filters.route.ZuulFallbackProvider;</span><br><span class="line">import org.springframework.cloud.netflix.zuul.filters.route.apache.HttpClientRibbonCommandFactory;</span><br><span class="line">import org.springframework.cloud.netflix.zuul.filters.route.okhttp.OkHttpRibbonCommandFactory;</span><br><span class="line">import org.springframework.context.annotation.Bean;</span><br><span class="line">import org.springframework.context.annotation.Conditional;</span><br><span class="line">import org.springframework.context.annotation.Configuration;</span><br><span class="line"></span><br><span class="line">/**</span><br><span class="line"> * @author Dave Syer</span><br><span class="line"> *</span><br><span class="line"> */</span><br><span class="line">public class RibbonCommandFactoryConfiguration &#123;</span><br><span class="line"></span><br><span class="line">	@Configuration</span><br><span class="line">	@ConditionalOnRibbonRestClient</span><br><span class="line">	protected static class RestClientRibbonConfiguration &#123;</span><br><span class="line"></span><br><span class="line">		@Autowired(required = false)</span><br><span class="line">		private Set&lt;ZuulFallbackProvider&gt; zuulFallbackProviders = Collections.emptySet();</span><br><span class="line"></span><br><span class="line">		@Bean</span><br><span class="line">		@ConditionalOnMissingBean</span><br><span class="line">		public RibbonCommandFactory&lt;?&gt; ribbonCommandFactory(</span><br><span class="line">				SpringClientFactory clientFactory, ZuulProperties zuulProperties) &#123;</span><br><span class="line">			return new RestClientRibbonCommandFactory(clientFactory, zuulProperties,</span><br><span class="line">					zuulFallbackProviders);</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	@Configuration</span><br><span class="line">	@ConditionalOnRibbonOkHttpClient</span><br><span class="line">	@ConditionalOnClass(name = &quot;okhttp3.OkHttpClient&quot;)</span><br><span class="line">	protected static class OkHttpRibbonConfiguration &#123;</span><br><span class="line"></span><br><span class="line">		@Autowired(required = false)</span><br><span class="line">		private Set&lt;ZuulFallbackProvider&gt; zuulFallbackProviders = Collections.emptySet();</span><br><span class="line"></span><br><span class="line">		@Bean</span><br><span class="line">		@ConditionalOnMissingBean</span><br><span class="line">		public RibbonCommandFactory&lt;?&gt; ribbonCommandFactory(</span><br><span class="line">				SpringClientFactory clientFactory, ZuulProperties zuulProperties) &#123;</span><br><span class="line">			return new OkHttpRibbonCommandFactory(clientFactory, zuulProperties,</span><br><span class="line">					zuulFallbackProviders);</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	@Configuration</span><br><span class="line">	@ConditionalOnRibbonHttpClient</span><br><span class="line">	protected static class HttpClientRibbonConfiguration &#123;</span><br><span class="line"></span><br><span class="line">		@Autowired(required = false)</span><br><span class="line">		private Set&lt;ZuulFallbackProvider&gt; zuulFallbackProviders = Collections.emptySet();</span><br><span class="line"></span><br><span class="line">		@Bean</span><br><span class="line">		@ConditionalOnMissingBean</span><br><span class="line">		public RibbonCommandFactory&lt;?&gt; ribbonCommandFactory(</span><br><span class="line">				SpringClientFactory clientFactory, ZuulProperties zuulProperties) &#123;</span><br><span class="line">			return new HttpClientRibbonCommandFactory(clientFactory, zuulProperties, zuulFallbackProviders);</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	@Target(&#123; ElementType.TYPE, ElementType.METHOD &#125;)</span><br><span class="line">	@Retention(RetentionPolicy.RUNTIME)</span><br><span class="line">	@Documented</span><br><span class="line">	@Conditional(OnRibbonHttpClientCondition.class)</span><br><span class="line">	@interface ConditionalOnRibbonHttpClient &#123; &#125;</span><br><span class="line"></span><br><span class="line">	private static class OnRibbonHttpClientCondition extends AnyNestedCondition &#123;</span><br><span class="line">		public OnRibbonHttpClientCondition() &#123;</span><br><span class="line">			super(ConfigurationPhase.PARSE_CONFIGURATION);</span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">		@Deprecated //remove in Edgware&quot;</span><br><span class="line">		@ConditionalOnProperty(name = &quot;zuul.ribbon.httpclient.enabled&quot;, matchIfMissing = true)</span><br><span class="line">		static class ZuulProperty &#123;&#125;</span><br><span class="line"></span><br><span class="line">		@ConditionalOnProperty(name = &quot;ribbon.httpclient.enabled&quot;, matchIfMissing = true)</span><br><span class="line">		static class RibbonProperty &#123;&#125;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	@Target(&#123; ElementType.TYPE, ElementType.METHOD &#125;)</span><br><span class="line">	@Retention(RetentionPolicy.RUNTIME)</span><br><span class="line">	@Documented</span><br><span class="line">	@Conditional(OnRibbonOkHttpClientCondition.class)</span><br><span class="line">	@interface ConditionalOnRibbonOkHttpClient &#123; &#125;</span><br><span class="line"></span><br><span class="line">	private static class OnRibbonOkHttpClientCondition extends AnyNestedCondition &#123;</span><br><span class="line">		public OnRibbonOkHttpClientCondition() &#123;</span><br><span class="line">			super(ConfigurationPhase.PARSE_CONFIGURATION);</span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">		@Deprecated //remove in Edgware&quot;</span><br><span class="line">		@ConditionalOnProperty(&quot;zuul.ribbon.okhttp.enabled&quot;)</span><br><span class="line">		static class ZuulProperty &#123;&#125;</span><br><span class="line"></span><br><span class="line">		@ConditionalOnProperty(&quot;ribbon.okhttp.enabled&quot;)</span><br><span class="line">		static class RibbonProperty &#123;&#125;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	@Target(&#123; ElementType.TYPE, ElementType.METHOD &#125;)</span><br><span class="line">	@Retention(RetentionPolicy.RUNTIME)</span><br><span class="line">	@Documented</span><br><span class="line">	@Conditional(OnRibbonRestClientCondition.class)</span><br><span class="line">	@interface ConditionalOnRibbonRestClient &#123; &#125;</span><br><span class="line"></span><br><span class="line">	private static class OnRibbonRestClientCondition extends AnyNestedCondition &#123;</span><br><span class="line">		public OnRibbonRestClientCondition() &#123;</span><br><span class="line">			super(ConfigurationPhase.PARSE_CONFIGURATION);</span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">		@Deprecated //remove in Edgware&quot;</span><br><span class="line">		@ConditionalOnProperty(&quot;zuul.ribbon.restclient.enabled&quot;)</span><br><span class="line">		static class ZuulProperty &#123;&#125;</span><br><span class="line"></span><br><span class="line">		@ConditionalOnProperty(&quot;ribbon.restclient.enabled&quot;)</span><br><span class="line">		static class RibbonProperty &#123;&#125;</span><br><span class="line">	&#125;</span><br><span class="line">	</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
解读:我们很清楚Zuul的负载均衡实现就通过Ribbon的实现,所以Http客户端的配置自然也是对Ribbon组件的配置,源代码中很明显都是通过@Conditional实现了条件加载,其中该类提供了不止一种的Http客户端(RestClient,默认的HttpClient,OkhttpClient)配置实现.要想确保改造成功,则需要在其内部类OkHttpRibbonConfiguration中的RibbonCommandFactory打上端点,看有没有进即可.</li>
</ol>
<h2 id="Feign中Okhttp应用"><a href="#Feign中Okhttp应用" class="headerlink" title="Feign中Okhttp应用"></a>Feign中Okhttp应用</h2><p>大家也都知道Fegin的底层也是Ribbon,上述方式可以解决,但是更推荐Feign端开始解决.</p>
<ol>
<li>首先在服务调用方加入以下依赖<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">&lt;!-- feign整合OkHttp --&gt;</span><br><span class="line">&lt;dependency&gt;</span><br><span class="line">	&lt;groupId&gt;io.github.openfeign&lt;/groupId&gt;</span><br><span class="line">	&lt;artifactId&gt;feign-okhttp&lt;/artifactId&gt;</span><br><span class="line">&lt;/dependency&gt;</span><br></pre></td></tr></table></figure></li>
<li>在application.yml文件配置<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">feign:</span><br><span class="line">  httpclient:</span><br><span class="line">      enabled: false</span><br><span class="line">  okhttp:</span><br><span class="line">      enabled: true</span><br></pre></td></tr></table></figure></li>
<li>测试是否配置成功<br>a, 启动服务进行接口调用,可以分析日志<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">[2018-01-30 13:04:03.398] [DEBUG] [restartedMain] o.s.c.e.PropertySourcesPropertyResolver -Found</span><br><span class="line"> key &#x27;feign.okhttp.enabled&#x27; in [applicationConfigurationProperties] with type [String]</span><br><span class="line"></span><br><span class="line">[2018-01-30 13:04:03.398] [DEBUG] [restartedMain] o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean</span><br><span class="line"> &#x27;autoConfigurationReport&#x27;</span><br><span class="line"></span><br><span class="line">[2018-01-30 13:04:03.398] [DEBUG] [restartedMain] o.s.c.a.ConfigurationClassBeanDefinitionReader - Registered bean definition for imported</span><br><span class="line"> class &#x27;org.springframework.cloud.netflix.feign.ribbon.FeignRibbonClientAutoConfiguration$OkHttpFeignLoadBalancedConfiguration&#x27;</span><br><span class="line"></span><br><span class="line">[2018-01-30 13:04:03.399] [DEBUG] [restartedMain] o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean</span><br><span class="line"> &#x27;org.springframework.boot.autoconfigure.condition.BeanTypeRegistry&#x27;</span><br><span class="line"></span><br><span class="line">[2018-01-30 13:04:03.399] [DEBUG] [restartedMain] o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean</span><br><span class="line"> &#x27;org.springframework.boot.autoconfigure.condition.BeanTypeRegistry&#x27;</span><br><span class="line"></span><br><span class="line">[2018-01-30 13:04:03.399] [DEBUG] [restartedMain] o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean</span><br><span class="line"> &#x27;autoConfigurationReport&#x27;</span><br><span class="line"></span><br><span class="line">[2018-01-30 13:04:03.399] [DEBUG] [restartedMain] o.s.c.a.ConfigurationClassBeanDefinitionReader -Registering</span><br><span class="line"> bean definition for @Bean method org.springframework.cloud.netflix.feign.ribbon.FeignRibbonClientAutoConfiguration$OkHttpFeignLoadBalancedConfigur</span><br></pre></td></tr></table></figure>
如上述日志文件Found key ‘feign.okhttp.enabled’可以看出替换成功.<br>b, 分析调用源码来测试是否成功替换成Okhttp<br>其自动装配源码在FeignRibbonClientAutoConfiguration中<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">@ConditionalOnClass(&#123; ILoadBalancer.class, Feign.class &#125;)</span><br><span class="line">@Configuration</span><br><span class="line">@AutoConfigureBefore(FeignAutoConfiguration.class)</span><br><span class="line">//Order is important here, last should be the default, first should be optional</span><br><span class="line">@Import(&#123; HttpClientFeignLoadBalancedConfiguration.class,</span><br><span class="line">		OkHttpFeignLoadBalancedConfiguration.class,</span><br><span class="line">		DefaultFeignLoadBalancedConfiguration.class &#125;)</span><br><span class="line">public class FeignRibbonClientAutoConfiguration &#123;</span><br><span class="line"></span><br><span class="line">    @Bean</span><br><span class="line">    @ConditionalOnMissingBean</span><br><span class="line">    public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,</span><br><span class="line">            SpringClientFactory clientFactory) &#123;</span><br><span class="line">	    //Feign默认的客户端是Client.Default</span><br><span class="line">        return new LoadBalancerFeignClient(new Client.Default(null, null),</span><br><span class="line">                cachingFactory, clientFactory);</span><br><span class="line">    &#125;</span><br><span class="line">	 //省略类中的内容...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
从该类中的注释可以看出,DefaultFeignLoadBalancedConfiguration.class是默认配置,HttpClientFeignLoadBalancedConfiguration.class是可选的.<br>Debug启动服务并进行远程Feign的一路调用发现,会走如下的方法:<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line">final class SynchronousMethodHandler implements MethodHandler &#123;</span><br><span class="line"></span><br><span class="line"> @Override</span><br><span class="line">  public Object invoke(Object[] argv) throws Throwable &#123;</span><br><span class="line">    RequestTemplate template = buildTemplateFromArgs.create(argv);</span><br><span class="line">    Retryer retryer = this.retryer.clone();</span><br><span class="line">    while (true) &#123;</span><br><span class="line">      try &#123;</span><br><span class="line">        return executeAndDecode(template);</span><br><span class="line">      &#125; catch (RetryableException e) &#123;</span><br><span class="line">        retryer.continueOrPropagate(e);</span><br><span class="line">        if (logLevel != Logger.Level.NONE) &#123;</span><br><span class="line">          logger.logRetry(metadata.configKey(), logLevel);</span><br><span class="line">        &#125;</span><br><span class="line">        continue;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">  Object executeAndDecode(RequestTemplate template) throws Throwable &#123;</span><br><span class="line">    Request request = targetRequest(template);</span><br><span class="line"></span><br><span class="line">    if (logLevel != Logger.Level.NONE) &#123;</span><br><span class="line">      logger.logRequest(metadata.configKey(), logLevel, request);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    Response response;</span><br><span class="line">    long start = System.nanoTime();</span><br><span class="line">    try &#123;</span><br><span class="line">      response = client.execute(request, options);</span><br><span class="line">      // ensure the request is set. TODO: remove in Feign 10</span><br><span class="line">      response.toBuilder().request(request).build();</span><br><span class="line">    &#125; catch (IOException e) &#123;</span><br><span class="line">      if (logLevel != Logger.Level.NONE) &#123;</span><br><span class="line">        logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime(start));</span><br><span class="line">      &#125;</span><br><span class="line">      throw errorExecuting(request, e);</span><br><span class="line">    &#125;</span><br><span class="line">    //省略代码......</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
我们发现SynchronousMethodHandler中的invoke()方法又去调用executeAndDecode()方法,在执行下列一段代码的时候,你会发现到底是用哪个Http客户端进行调用<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">response = client.execute(request, options);  </span><br></pre></td></tr></table></figure>
Client是Feign的Client接口声明,这时候会有好几个实现:Default,LoadBalancerFeignClinet,OkHttpClient,TraceFeignClient.<br>这下明显了,如果你的classpath下有okhttp的依赖,则会走feign.okhttp.OkHttpClient.execute()方法,如果没有依赖,就会走feign.Client.Default.execute()方法.<br>okhttp没什么说的,可以看一下默认的Feign Client - Default中的execute()代码片段:<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">@Override</span><br><span class="line">   public Response execute(Request request, Options options) throws IOException &#123;</span><br><span class="line">     HttpURLConnection connection = convertAndSend(request, options);</span><br><span class="line">     return convertResponse(connection).toBuilder().request(request).build();</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure>
由上面代码片段可以得出,Spring Cloud默认Feign的客户端是HttpURLConnection构建的.<br>需要注意的是,无论Feign是哪种Client,默认都是开启的,重点在于classpath下的依赖,而不是application.yml里的开关配置.之所以配置开关是因为关闭掉无用的Client.</li>
</ol>
<h2 id="默认配置解说"><a href="#默认配置解说" class="headerlink" title="默认配置解说"></a>默认配置解说</h2><p>Feign 读取时间配置：<br>默认读取连接时间10s,默认连接读取时间60s<br>源码:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">public final class Request &#123;</span><br><span class="line"></span><br><span class="line">	 public static class Options &#123; </span><br><span class="line"></span><br><span class="line">	 	private final int connectTimeoutMillis;</span><br><span class="line">   		private final int readTimeoutMillis;</span><br><span class="line"></span><br><span class="line">	    public Options(int connectTimeoutMillis, int readTimeoutMillis) &#123;</span><br><span class="line">	      this.connectTimeoutMillis = connectTimeoutMillis;</span><br><span class="line">	      this.readTimeoutMillis = readTimeoutMillis;</span><br><span class="line">	    &#125;</span><br><span class="line"></span><br><span class="line">        //默认构造器时间</span><br><span class="line">	 	public Options() &#123;</span><br><span class="line">      		this(10 * 1000, 60 * 1000);</span><br><span class="line">    	&#125;</span><br><span class="line">	 &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>手动设置默认的读取时间和连接时间:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">@Bean</span><br><span class="line">public Request.Options options() &#123;</span><br><span class="line">	 Request.Options options = new  Request.Options(5 * 1000, 5 * 1000);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>手动配置Okhttp3</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">@Configuration</span><br><span class="line">@ConditionalOnClass(Feign.class)</span><br><span class="line">@AutoConfigureBefore(FeignAutoConfiguration.class)</span><br><span class="line">public class FeignOkHttpConfig &#123;</span><br><span class="line"></span><br><span class="line">    @Autowired</span><br><span class="line">    OkHttpLoggingInterceptor okHttpLoggingInterceptor;</span><br><span class="line"></span><br><span class="line">    @Bean</span><br><span class="line">    public okhttp3.OkHttpClient okHttpClient()&#123;</span><br><span class="line">        return new okhttp3.OkHttpClient.Builder()</span><br><span class="line">            .readTimeout(60, TimeUnit.SECONDS)  //设置读取超时时间</span><br><span class="line">            .connectTimeout(60, TimeUnit.SECONDS) //设置连接超时时间</span><br><span class="line">            .writeTimeout(120, TimeUnit.SECONDS) </span><br><span class="line">            .connectionPool(new ConnectionPool())</span><br><span class="line">            // .addInterceptor();  //添加请求拦截器</span><br><span class="line">            .build();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>参考文献:<br><a target="_blank" rel="noopener" href="http://cloud.spring.io/spring-cloud-static/Edgware.SR1/single/spring-cloud.html">官方文档</a><br><a target="_blank" rel="noopener" href="http://blog.springcloud.cn/sc/sc-feign-4xx/">Feign产生的问题</a><br><a target="_blank" rel="noopener" href="http://blog.springcloud.cn/sc/sc-w-feign/">Feign源码解读</a></p>
</section>
    <!-- Tags START -->
    
      <div class="tags">
        <span>Tags:</span>
        
  <a href="/tags#Spring Cloud" >
    <span class="tag-code">Spring Cloud</span>
  </a>

      </div>
    
    <!-- Tags END -->
    <!-- NAV START -->
    
  <div class="nav-container">
    <!-- reverse left and right to put prev and next in a more logic postition -->
    
      <a class="nav-left" href="/2017/12/15/ES6%E6%96%B0%E7%9A%84%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B-generator/">
        <span class="nav-arrow">← </span>
        
          ES6新的数据类型-generator
        
      </a>
    
    
      <a class="nav-right" href="/2018/01/30/%E6%B5%85%E8%B0%88%E5%8C%BF%E5%90%8D%E5%87%BD%E6%95%B0,Lambda,%E9%97%AD%E5%8C%85(Closure)/">
        
          浅谈匿名函数,Lambda和闭包(Closure)
        
        <span class="nav-arrow"> →</span>
      </a>
    
  </div>

    <!-- NAV END -->
    <!-- 打赏 START -->
    
      <div class="money-like">
        <div class="reward-btn">
          赏
          <span class="money-code">
            <span class="alipay-code">
              <div class="code-image"></div>
              <b>使用支付宝打赏</b>
            </span>
            <span class="wechat-code">
              <div class="code-image"></div>
              <b>使用微信打赏</b>
            </span>
          </span>
        </div>
        <p class="notice">若你觉得我的文章对你有帮助，欢迎点击上方按钮对我打赏</p>
      </div>
    
    <!-- 打赏 END -->
    <!-- 二维码 START -->
    
      <div class="qrcode">
        <canvas id="share-qrcode"></canvas>
        <p class="notice">扫描二维码，分享此文章</p>
      </div>
    
    <!-- 二维码 END -->
    
      <!-- Utterances START -->
      <div id="utterances"></div>
      <script src="https://utteranc.es/client.js"
        repo="ltyeamin/blogtalks"
        issue-term="pathname"
        theme="github-light"
        crossorigin="anonymous"
        async></script>    
      <!-- Utterances END -->
    
  </article>
  <!-- Article END -->
  <!-- Catalog START -->
  
    <aside class="catalog-container">
  <div class="toc-main">
    <strong class="toc-title">Catalog</strong>
    
      <ol class="toc-nav"><li class="toc-nav-item toc-nav-level-2"><a class="toc-nav-link" href="#OkHttp%E5%9C%A8Zuul%E4%B8%AD%E7%9A%84%E5%BA%94%E7%94%A8"><span class="toc-nav-text">OkHttp在Zuul中的应用</span></a></li><li class="toc-nav-item toc-nav-level-2"><a class="toc-nav-link" href="#Feign%E4%B8%ADOkhttp%E5%BA%94%E7%94%A8"><span class="toc-nav-text">Feign中Okhttp应用</span></a></li><li class="toc-nav-item toc-nav-level-2"><a class="toc-nav-link" href="#%E9%BB%98%E8%AE%A4%E9%85%8D%E7%BD%AE%E8%A7%A3%E8%AF%B4"><span class="toc-nav-text">默认配置解说</span></a></li></ol>
    
  </div>
</aside>
  
  <!-- Catalog END -->
</main>

<script>
  (function () {
    var url = 'http://example.com/2018/01/30/Spring Cloud-使用高性能的OkHttp库/';
    var banner = ''
    if (banner !== '' && banner !== 'undefined' && banner !== 'null') {
      $('#article-banner').css({
        'background-image': 'url(' + banner + ')'
      })
    } else {
      $('#article-banner').geopattern(url)
    }
    $('.header').removeClass('fixed-header')

    // error image
    $(".markdown-content img").on('error', function() {
      $(this).attr('src', '/css/images/error_icon.png')
      $(this).css({
        'cursor': 'default'
      })
    })

    // zoom image
    $(".markdown-content img").on('click', function() {
      var src = $(this).attr('src')
      if (src !== '/css/images/error_icon.png') {
        var imageW = $(this).width()
        var imageH = $(this).height()

        var zoom = ($(window).width() * 0.95 / imageW).toFixed(2)
        zoom = zoom < 1 ? 1 : zoom
        zoom = zoom > 2 ? 2 : zoom
        var transY = (($(window).height() - imageH) / 2).toFixed(2)

        $('body').append('<div class="image-view-wrap"><div class="image-view-inner"><img src="'+ src +'" /></div></div>')
        $('.image-view-wrap').addClass('wrap-active')
        $('.image-view-wrap img').css({
          'width': `${imageW}`,
          'transform': `translate3d(0, ${transY}px, 0) scale3d(${zoom}, ${zoom}, 1)`
        })
        $('html').css('overflow', 'hidden')

        $('.image-view-wrap').on('click', function() {
          $(this).remove()
          $('html').attr('style', '')
        })
      }
    })
  })();
</script>


  <script>
    var qr = new QRious({
      element: document.getElementById('share-qrcode'),
      value: document.location.href
    });
  </script>






    <div class="scroll-top">
  <span class="arrow-icon"></span>
</div>
    <footer class="app-footer">
  <p class="copyright">
    &copy; 2024 | Proudly powered by <a href="https://hexo.io" target="_blank">Hexo</a>
    <br>
    Theme by <a target="_blank" rel="noopener" href="https://github.com/ltyeamin">tong.li</a>
  </p>
</footer>

<script>
  function async(u, c) {
    var d = document, t = 'script',
      o = d.createElement(t),
      s = d.getElementsByTagName(t)[0];
    o.src = u;
    if (c) { o.addEventListener('load', function (e) { c(null, e); }, false); }
    s.parentNode.insertBefore(o, s);
  }
</script>
<script>
  async("https://cdn.staticfile.org/fastclick/1.0.6/fastclick.min.js", function(){
    FastClick.attach(document.body);
  })
</script>

<script>
  var hasLine = 'true';
  async("https://cdn.staticfile.org/highlight.js/9.12.0/highlight.min.js", function(){
    $('figure pre').each(function(i, block) {
      var figure = $(this).parents('figure');
      if (hasLine === 'false') {
        figure.find('.gutter').hide();
      }
      hljs.configure({useBR: true});
      var lang = figure.attr('class').split(' ')[1] || 'code';
      var codeHtml = $(this).html();
      var codeTag = document.createElement('code');
      codeTag.className = lang;
      codeTag.innerHTML = codeHtml;
      $(this).attr('class', '').empty().html(codeTag);
      figure.attr('data-lang', lang.toUpperCase());
      hljs.highlightBlock(block);
    });
  })
</script>
<!-- Baidu Tongji -->



<script src='https://cdn.staticfile.org/mermaid/8.11.2/mermaid.min.js'></script>



<script src="/js/script.js"></script>


  </body>
</html>